﻿using System;
using System.IO;
using System.Linq;
using FirstFloor.Documents.Fonts;

namespace FirstFloor.Documents.Pdf.Fonts
{
    /// <summary>
    /// Represents the font based on the TrueType font format.
    /// </summary>
    internal class TrueTypeFont
        : Type1Font
    {
        private GlyphTypeface typeface;

        /// <summary>
        /// Initializes a new instance of the <see cref="TrueTypeFont"/> class.
        /// </summary>
        /// <param name="fontDictionary">The font dictionary.</param>
        public TrueTypeFont(DictionaryObject fontDictionary)
            : base(fontDictionary)
        {
            // parse style simulation
            var i = this.FontName.IndexOf(',');
            if (i != -1) {
                var style = this.FontName.Substring(i + 1);
                if (style == "Bold") {
                    this.StyleSimulation = System.Windows.Media.StyleSimulations.BoldSimulation;
                }
                else if (style == "Italic") {
                    this.StyleSimulation = System.Windows.Media.StyleSimulations.ItalicSimulation;
                }
                else if (style == "BoldItalic") {
                    this.StyleSimulation = System.Windows.Media.StyleSimulations.BoldItalicSimulation;
                }
                this.FontName = this.FontName.Substring(0, i);
            }

            using (var fontFile = GetFontFile()) {
                if (fontFile != null) {
                    var fontInfo = FontParser.ParseFontInfo(fontFile);
                    this.typeface = FontParser.ParseFontFace(fontInfo, 0);
                }
            }
        }

        /// <summary>
        /// Gets the font file.
        /// </summary>
        /// <returns></returns>
        public override Stream GetFontFile()
        {
            DictionaryObject fontFile;
            if (!this.FontDescriptor.TryGet<DictionaryObject>("FontFile2", out fontFile)) {
                if (!this.FontDescriptor.TryGet<DictionaryObject>("FontFile3", out fontFile)) {
                    return null;
                }
                if (fontFile.Get<string>("Subtype") != "OpenType") {
                    return null;
                }
            }

            var stream = fontFile.Owner.ParseStreamObject(fontFile);
            return stream.OpenFilteredStream();
        }

        /// <summary>
        /// Gets the glyph index for specified glyph name.
        /// </summary>
        /// <param name="glyphName">Name of the glyph.</param>
        /// <returns></returns>
        protected override ushort? GetGlyphIndex(string glyphName)
        {
            // get (3,1) cmap subtable
            var cmap = this.typeface.CharacterToGlyphMaps.FirstOrDefault(m => m.PlatformId == 3 && m.EncodingId == 1);
            if (cmap != null) {
                char c;
                // map glyph name to unicode value
                if (GlyphList.TryGetCodePoint(glyphName, out c)) {
                    // map unicode value to glyph index
                    return cmap[c];
                }
                else {
                    // TODO: lookup glyph name in the 'post' table
                }
            }
            else {
                // get (1,0) cmap subtable
                cmap = this.typeface.CharacterToGlyphMaps.FirstOrDefault(m => m.PlatformId == 1 && m.EncodingId == 0);
                if (cmap != null) {
                    // map glyph name back to character code
                    int charCode;
                    if (CharacterToGlyphNameMap.MacOSRomanEncoding.TryGetChar(glyphName, out charCode)) {
                        // map character code to index
                        return cmap[charCode];
                    }
                    else {
                        // TODO: look glyph name in the 'post' table
                    }
                }
            }

            // character cannot be mapped
            return null;
        }

        /// <summary>
        /// Determines whether a font file is embedded in the specified font dictionary.
        /// </summary>
        /// <param name="fontDictionary">The font dictionary.</param>
        /// <returns>
        /// 	<c>true</c> if a font file is embedded; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsFontFileEmbedded(DictionaryObject fontDictionary)
        {
            var fontDescriptor = fontDictionary.Get<DictionaryObject>("FontDescriptor", null);
            if (fontDescriptor != null) {
                if (fontDescriptor.ContainsKey("FontFile2")) {
                    return true;
                }

                var fontFile = fontDescriptor.Get<DictionaryObject>("FontFile3", null);
                if (fontFile != null) {
                    return fontFile.Get<string>("Subtype") == "OpenType";
                }
            }
            return false;
        }
    }
}
